package com.semnox.androidgame.test;

import java.text.DecimalFormat;

import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;

public class MainThread extends Thread {

	private static final String TAG = MainThread.class.getSimpleName();
	//surface that can access physical surface
	private SurfaceHolder surfaceHolder;
	//actual view that handles inputs and draws to the surface
	private MainGamePanel gamePanel;
	//flag to hold game state
	private boolean running;

	//desired fps
	private final static int MAX_FPS=60;
	//maximum number of frames to be skipped
	private final static int MAX_FRAME_SKIPS=6;
	//the frame period
	private final static int FRAME_PERIOD=1000/MAX_FPS;

	//stuff for stats
	private DecimalFormat df = new DecimalFormat("0.##"); //2dp
	//reading stats every second
	private final int STAT_INTERVAL=1000; //ms
	//the avg will be calculated by storing last n in FPS
	private final static int FPS_HISTORY_NR=10;
	//last time the status was stored
	private long lastStatusStore=0;
	//status time counter
	private long statusIntervalTimer=01;
	//number of frames skipped since game started
	private long totalFramesSkipped=01;
	//number of frames skipped in a store cycle (1 sec)
	private long framesSkippedPerStatCycle=01;
	//number of rendered frames in an interval
	private int frameCountPerStatCycle=0;
	private long totalFrameCount=01;
	//last fps vales
	private double fpsStore[];
	//the number of times the stat has been read
	private long statsCount=0;
	//the average fps since game started
	private double averageFps=0.0;

	public void setRunning(boolean running) {
		this.running = running;
	}

	public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
		super();
		this.surfaceHolder = surfaceHolder;
		this.gamePanel = gamePanel;
	}

	
	private void initTimingElements() {
		//initialise timing elements
		fpsStore=new double[FPS_HISTORY_NR];
		for (int i=0;i<FPS_HISTORY_NR; i++) {
			fpsStore[i]=0.0;
		}
		Log.d(TAG+".initTimingElements()", "Timing elements for stats initialised");
	}

	/* The statistics - it is called every cycle, it checks if time since last
	 * store is greater than the statistics gathering period (1 sec) and if so
	 * it calculates the FPS for the last period and stores it.
	 * It tracks the number of frames per period. The number of frames since
	 * the start of the period are summed up and the calculation takes part
	 * only if the next period and the frame count is reset to 0.
	 */

	private void storeStats() {
		frameCountPerStatCycle++;
		totalFrameCount++;

		//check actual time
		statusIntervalTimer+=(System.currentTimeMillis()-statusIntervalTimer);

		if (statusIntervalTimer>=lastStatusStore+STAT_INTERVAL) {
			//calculate actual fps check interval
			double actualFps=(double)(frameCountPerStatCycle/STAT_INTERVAL/1000);

			//stores latest fps in array
			fpsStore[(int) statsCount%FPS_HISTORY_NR]=actualFps;

			//increase the number of times statistics was calculated
			statsCount++;

			double totalFps=0.0;
			//sum up stored fps values
			for (int i =0;i<FPS_HISTORY_NR;i++) {
				totalFps+=fpsStore[i];
			}

			//obtain the average
			if(statsCount<FPS_HISTORY_NR) {
				//in case of the first 10 triggers
				averageFps=totalFps/statsCount;
			} else {
				averageFps=totalFps/FPS_HISTORY_NR;
			}
			//saving the number of frames skipped
			totalFramesSkipped+=framesSkippedPerStatCycle;
			//resetting the counters after a status record (1sec)
			framesSkippedPerStatCycle=0;
			statusIntervalTimer=0;
			frameCountPerStatCycle=0;

			statusIntervalTimer=System.currentTimeMillis();
			lastStatusStore=statusIntervalTimer;
			Log.d(TAG, "Average FPS:"+df.format(averageFps));
			gamePanel.setAvgFps("FPS: "+df.format(averageFps));
		}
	}
	
	@Override
	public void run() {
		Canvas canvas;
		Log.d(TAG, "Starting game loop");
		//initialise timing elements
		initTimingElements();

		long beginTime; //time when cycle began
		long timeDiff; //time it took for cycle to execute
		int sleepTime; //ms to sleep (<0 if we're behind)
		int framesSkipped; //number of frames being skipped

		sleepTime=0;

		while(running) {
			canvas = null; 
			//try locking canvas for exclusive pixel editing on the surface
			try {
				canvas = this.surfaceHolder.lockCanvas();
				synchronized (surfaceHolder) {
					beginTime=System.currentTimeMillis();
					framesSkipped=0; //resetting the frames skipped
					//update game state
					this.gamePanel.update();
					//render state to screen
					//draws the canvas on the panel
					this.gamePanel.render(canvas);
					//calculate cycle time
					timeDiff=System.currentTimeMillis()-beginTime;
					//calculate sleep time
					sleepTime = (int)(FRAME_PERIOD-timeDiff);

					if (sleepTime>0) {
						//if sleepTime>0 its okay
						try {
							//send the thread to sleep for short period use full for battery saving
							Thread.sleep(sleepTime);
						} catch (InterruptedException e) {}
					}

					while (sleepTime<0 && framesSkipped<MAX_FRAME_SKIPS) {
						//need to catch up update without rendering
						this.gamePanel.update();
						//add frame period to check if in next frame
						sleepTime+=FRAME_PERIOD;
						framesSkipped++;
					}
					
					if (framesSkipped>0) {
						Log.d(TAG, "Skipped: "+framesSkipped);
					}
					//for statistics
					framesSkippedPerStatCycle+=framesSkipped;
					//store statistics
					storeStats();
				}
			} finally {
				//in case of an exception the surface is not left in an inconsistent state
				if (canvas != null) {
					surfaceHolder.unlockCanvasAndPost(canvas);
				}
			} // end finally
		}
	}
	
}
